﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Couchbase;
using Enyim.Caching.Memcached;
using Enyim.Caching.Memcached.Results;
using Newtonsoft.Json;

namespace GuoZhiQi.MiddleCache
{
    [Serializable]
    public abstract class CouchBaseObject : CouchBaseObjectLight, ICouchBaseObject
    {

        [NonSerialized]
        internal CouchbaseClient _client;

        /// <summary>
        /// CouchBase  客户端
        /// </summary>
        [JsonIgnore]
        public CouchbaseClient Client
        {
            get
            {
                return _client;
            }
        }

        [NonSerialized]
        internal TimeSpan? _expiresAfter = null;

        /// <summary>
        /// 设置timeSpan类型的过期时间
        /// </summary>
        [JsonIgnore]
        public TimeSpan ExpiresAfter
        {
            set
            {
                this._expiresAfter = value;
                this._expiresOn = null;
            }
        }

        [NonSerialized]
        internal DateTime? _expiresOn = null;

        /// <summary>
        /// 设置DateTime类型的过期时间
        /// </summary>
        [JsonIgnore]
        public DateTime? ExpiresOn
        {
            set
            {
                this._expiresAfter = null;
                this._expiresOn = value;
            }
        }

             /// <summary>
        /// Creates new instance of this object.
        /// </summary>
        /// <param name="client">Membase client to work with.</param>
        public CouchBaseObject(CouchbaseClient client)
            : base()
        {
            this._client = client;

            if (this._client == null)
                throw new ArgumentNullException("Client");
        }

        public CouchBaseObject(CouchbaseClient client, string objectKey, bool tryLoadFromDB = false)
            : base(objectKey)
        {
            this._client = client;

            if (this._client == null)
                throw new ArgumentNullException("Client");

            if (tryLoadFromDB)
                this.Refresh();
        }


        public static CouchBaseObject FromMembaseLight(CouchBaseObjectLight obj, CouchbaseClient client = null)
        {
            CouchBaseObject objMem = (CouchBaseObject)obj;
            objMem._client = client;
            return objMem;
        }


        /// <summary>
        /// 更新CAS值
        /// </summary>
        /// <returns></returns>
        public virtual bool UpdateCAS()
        {
            if (this.Key == null)
            {
                throw new ArgumentNullException("key for couchbase is not exists",(Exception)null);
            }
            IGetOperationResult result = this._client.ExecuteGet(this.Key);
            if (!result.Success)
            {
                if (result.Exception != null)
                {
                    throw new CouchBaseException(this.Key, StoreMode.Add, "Error occured in couchbase", result.Exception);
                }
                return false;
            }
            else
            {
                this.CAS = result.Cas;
            }
            return true;
        }
        public virtual bool Refresh(bool withTouch = false)
        {
            if (this.Key == null)
                throw new ArgumentNullException("Key for Membase item not set yet.", (Exception)null);

            this.InvalidateJsonValue();
            IGetOperationResult result = this._client.ExecuteGet(this.Key);

            if (result.Success)
            {
                this.CAS = result.Cas;
                this.FromJson(result.Value.ToString());
                this.RefreshMembaseParams();

                if (withTouch)
                {
                    if (this._expiresAfter.HasValue)
                        this._client.Touch(this.Key, this._expiresAfter.Value);
                    else if (this._expiresOn.HasValue)
                        this._client.Touch(this.Key, this._expiresOn.Value);
                }
            }
            else if (result.Exception != null)
                throw new CouchBaseException(this.Key, "Error occured in membase.", result.Exception);

            this.JsonInSync = result.Success;
            return result.Success;
        }
        /// <summary>
        /// 往couchBase中添加一个对象 如果他的key不存在
        /// </summary>
        /// <returns></returns>
        public virtual bool Add()
        {
            IStoreOperationResult result = this.ExecuteStoreMethod(StoreMode.Add, false);
            if (result.Success)
                this.SaveMembaseParams();
            this.JsonInSync = result.Success;
            return result.Success;
        }
        /// <summary>
        /// Sets or adds object.
        /// </summary>
        /// <returns></returns>
        public virtual bool Set()
        {
            if (this.Key == null)
                throw new ArgumentNullException("Key for Membase item not set yet.", (Exception)null);

            IStoreOperationResult result = this.ExecuteStoreMethod(StoreMode.Set, false);
            if (result.Success)
                this.SaveMembaseParams();
            this.JsonInSync = result.Success;
            return result.Success;
        }

        /// <summary>
        /// Sets or adds object only if it has not changed since last refresh. 使用CAS
        /// </summary>
        /// <returns>Returns false if failed.</returns>
        public virtual bool SafeSet()
        {
            IStoreOperationResult result = this.ExecuteStoreMethod(StoreMode.Set, true);
            if (result.Success)
                this.SaveMembaseParams();
            this.JsonInSync = result.Success;
            return result.Success;
        }
        /// <summary>
        /// Replaces value of existing object in Membase.
        /// </summary>
        /// <returns>Returns false if object does not exist.</returns>
        public virtual bool Replace()
        {
            IStoreOperationResult result = this.ExecuteStoreMethod(StoreMode.Replace, false);
            if (result.Success)
                this.SaveMembaseParams();
            this.JsonInSync = result.Success;
            return result.Success;
        }
        /// <summary>
        /// Removes this object from Membase
        /// </summary>
        /// <returns>Returns false if object does not exist.</returns>
        public virtual bool Remove()
        {
            if (this.Key == null)
                throw new ArgumentNullException("Key for Membase item not set yet.", (Exception)null);

            IRemoveOperationResult result = this._client.ExecuteRemove(this.Key);

            if (result.Success)
                this.RemoveMembaseParams();
            else if (result.Exception != null)
                throw new CouchBaseException(this.Key, "Error occured in membase on remove operation.", result.Exception);

            this.JsonInSync = result.Success;
            return result.Success;
        }



        /// <summary>
        /// 在当前对象执行存储
        /// </summary>
        /// <param name="mode">Store mode to do.</param>
        /// <returns></returns>
        internal IStoreOperationResult ExecuteStoreMethod(StoreMode mode, bool useCas, string customValue = null)
        {
            if (this.Key == null)
                throw new ArgumentNullException("Key for Membase item not set yet.", (Exception)null);

            this.InvalidateJsonValue();

            string valueToStore = this.ToJson();
            if (customValue != null)
                valueToStore = customValue;

            IStoreOperationResult result;

            if (useCas)
            {
                if (this.CAS == 0)
                    throw new ArgumentNullException("CAS of object has not been refreshed yet.", (Exception)null);

                if (this._expiresAfter.HasValue)
                    result = this._client.ExecuteCas(mode, this.Key, valueToStore, this._expiresAfter.Value, this.CAS);
                else if (this._expiresOn.HasValue)
                    result = this._client.ExecuteCas(mode, this.Key, valueToStore, this._expiresOn.Value, this.CAS);
                else
                    result = this._client.ExecuteCas(mode, this.Key, valueToStore, this.CAS);
            }
            else
            {
                if (this._expiresAfter.HasValue)
                    result = this._client.ExecuteStore(mode, this.Key, valueToStore, this._expiresAfter.Value);
                else if (this._expiresOn.HasValue)
                    result = this._client.ExecuteStore(mode, this.Key, valueToStore, this._expiresOn.Value);
                else
                    result = this._client.ExecuteStore(mode, this.Key, valueToStore);
            }

            if (result.Success)
            {
                this.CAS = result.Cas;
            }
            else if (result.Exception != null)
                throw new CouchBaseException(this.Key, mode, "Error occured in membase.", result.Exception);

            return result;
        }






        /// <summary>
        /// Saves membase parameters in database when overriden. Override when using <see cref="IMembaseParameter"/> in inherited class.
        /// </summary>
        protected virtual void SaveMembaseParams()
        {
        }

        /// <summary>
        /// Removes membase parameters in database when overriden. Override when using <see cref="IMembaseParameter"/> in inherited class.
        /// </summary>
        protected virtual void RemoveMembaseParams()
        {
        }

        /// <summary>
        /// Refreshes membase parameters in database when overriden. Override when using <see cref="IMembaseParameter"/> in inherited class.
        /// </summary>
        protected virtual void RefreshMembaseParams()
        {
        }

    }
}
